﻿using System.Collections.Generic;
using System.Net;

namespace PacketRush.Core
{
    /// <summary>
    /// A Generic collection of constrained to types that implement INetConnection.
    /// </summary>
    public class NetConnectionCollection<T> : IList<T> where T: INetConnection
    {
        private List<T> _InternalList;
        private Dictionary<int, T> _InternalIdKeyedDict;
        private Dictionary<EndPoint, T> _InternalEndPointKeyedDict;

        /// <summary>
        /// Creates an instance of NetConnectionCollection
        /// </summary>
        /// <param name="initialCapacity">The initial capacity of this collection.</param>
        /// <param name="useIdDictLookup">Wether to use a dictionary for id lookups.</param>
        /// <param name="useEndPointDictLookup">Wether to use a dictionary for end point lookups.</param>
        public NetConnectionCollection(int initialCapacity, bool useIdDictLookup, bool useEndPointDictLookup)
        {
            _InternalList = new List<T>(initialCapacity);
            if(useIdDictLookup) _InternalIdKeyedDict = new Dictionary<int, T>(initialCapacity);
            if(useEndPointDictLookup) _InternalEndPointKeyedDict = new Dictionary<EndPoint, T>(initialCapacity);
        }

        /// <summary>
        /// Get this index of the given item.
        /// </summary>
        /// <param name="item">The item to get the index of.</param>
        /// <returns>The index of the given item.</returns>
        public int IndexOf(T item)
        {
            return _InternalList.IndexOf(item);
        }

        /// <summary>
        /// Insert an item at the given index.
        /// </summary>
        /// <param name="index">The index to insert at.</param>
        /// <param name="item">The item to insert.</param>
        public void Insert(int index, T item)
        {
            if(_InternalIdKeyedDict != null) _InternalIdKeyedDict.Add(item.Id, item);
            if(_InternalEndPointKeyedDict != null ) _InternalEndPointKeyedDict.Add(item.RemoteEndPoint, item);
            _InternalList.Insert(index, item);
        }

        /// <summary>
        /// Remove the item at the given index.
        /// </summary>
        /// <param name="index">The index to remove the item at.</param>
        public void RemoveAt(int index)
        {
            T connection = _InternalList[index];
            _InternalIdKeyedDict.Remove(connection.Id);
            _InternalEndPointKeyedDict.Remove(connection.RemoteEndPoint);
            _InternalList.RemoveAt(index);
        }

        /// <summary>
        /// Indexer.
        /// </summary>
        /// <param name="index">The zero-based index of the item to get or set.</param>
        /// <returns>The item at the given index.</returns>
        public T this[int index]
        {
            get
            {
                return _InternalList[index];
            }
            set
            {
                _InternalList[index] = value;
            }
        }

        /// <summary>
        /// Add an item.
        /// </summary>
        /// <param name="item">The item to add.</param>
        public void Add(T item)
        {
            if(_InternalIdKeyedDict != null) _InternalIdKeyedDict.Add(item.Id, item);
            if(_InternalEndPointKeyedDict != null) _InternalEndPointKeyedDict.Add(item.RemoteEndPoint, item);
            _InternalList.Add(item);
        }

        /// <summary>
        /// Clear all items.
        /// </summary>
        public void Clear()
        {
            if(_InternalIdKeyedDict != null) _InternalIdKeyedDict.Clear();
            if(_InternalEndPointKeyedDict != null) _InternalEndPointKeyedDict.Clear();
            _InternalList.Clear();
        }

        /// <summary>
        /// Test if the collection contains the given item.
        /// </summary>
        /// <param name="item">The item to test.</param>
        /// <returns>True if the collection contains the given item.</returns>
        public bool Contains(T item)
        {
            return _InternalList.Contains(item);
        }

        /// <summary>
        /// Copy the given array of items to this collection.
        /// </summary>
        /// <param name="array">The array of items to copy.</param>
        /// <param name="arrayIndex">The zero-based index in the array at which copy begins.</param>
        public void CopyTo(T[] array, int arrayIndex)
        {
            _InternalList.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// The number of items in this collection.
        /// </summary>
        public int Count
        {
            get { return _InternalList.Count; }
        }

        /// <summary>
        /// Wether this collection is a read-only collection.
        /// </summary>
        public bool IsReadOnly
        {
            get { return false; }
        }

        /// <summary>
        /// Remove the given item.
        /// </summary>
        /// <param name="item">The itme to remove.</param>
        /// <returns>True if the item was removed.</returns>
        public bool Remove(T item)
        {
            if(_InternalIdKeyedDict != null) _InternalIdKeyedDict.Remove(item.Id);
            if(_InternalEndPointKeyedDict != null) _InternalEndPointKeyedDict.Remove(item.RemoteEndPoint);

            return _InternalList.Remove(item);
        }

        /// <summary>
        /// Returns an enumerator to iterate over this collection.
        /// </summary>
        /// <returns>An enumerator.</returns>
        public IEnumerator<T> GetEnumerator()
        {
            return _InternalList.GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator to iterate over this collection.
        /// </summary>
        /// <returns>An enumerator.</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _InternalList.GetEnumerator();
        }

        /// <summary>
        /// Get a connection in this collection by id
        /// </summary>
        /// <param name="clientConnectionId">The connection id to get the item by.</param>
        /// <returns>The connection or null of the item does not exist in the collection.</returns>
        public T GetById(int clientConnectionId)
        {
            if (_InternalIdKeyedDict == null)
            {
                _InternalList.Find((conn) => conn.Id == clientConnectionId);
            }
            else
            {
                if (_InternalIdKeyedDict.ContainsKey(clientConnectionId))
                {
                    return _InternalIdKeyedDict[clientConnectionId];
                }
            }

            return default(T);
        }

        /// <summary>
        /// Get a connection in this collection by remote end point.
        /// </summary>
        /// <param name="endPoint">The connections remote end point to get the item by.</param>
        /// <returns>The connection or null if the item does not exist in the collection.</returns>
        public T GetByRemoteEndPoint(EndPoint endPoint)
        {
            if (_InternalEndPointKeyedDict == null)
            {
                return _InternalList.Find((conn) => conn.RemoteEndPoint.Equals(endPoint));
            }
            else
            {
                if (_InternalEndPointKeyedDict.ContainsKey(endPoint))
                {
                    return _InternalEndPointKeyedDict[endPoint];
                }
            }

            return default(T);
        }
    }
}